<?php
require_once 'models/catalog.inc';
require_once 'models/TMSPublicableItemModel.inc';

class TMSProductItem extends TCandyItem {

	private $_discounts_ = null;
	
	private $_discount_ = false;
	
	public function __get($nm){
		switch ($nm){
			case 'State':{
				if ($this->Reserved && ($this->Quantaty <= $this->Reserved))
					if ($this->Model->AllowReservedOrder)
						return  (string)TMSProductState::AvailableState();
					else
						return (string)TMSProductState::UnavailableState();
				return parent::__get($nm);
			}break;
			case 'AllowOrder':return $this->State != (string)TMSProductState::UnavailableState() || parent::__get($nm);break;
			case 'Discounts':{
				if (!$this->_discounts_)
					$this->_discounts_ = $this->Model->ItemDiscounts($this) + $this->Nomenclature->Discounts;
				return $this->_discounts_;
			}break;
			case 'DiscountAmount':{
				if ($this->_discount_ === false){
					$this->_discount_ = 0;
					foreach ($this->Discounts as $d)
						if ($d instanceof TMSNomenclatureDiscountItem)
							$this->_discount_ += $d->Calculate($p,$this);
						else
							$this->_discount_ += $d->Calculate($p);
				}
				return $this->_discount_;
			}break;
			case 'DiscountedPrice':return ($this->DiscountAmount > 0)?($this->Price - $this->DiscountAmount):null;break;			
			default:{
				$v = parent::__get($nm);	
				if (in_array($nm,array('Name','Price','KitPrice')) && !$v){
					$n = parent::__get('Nomenclature');				
					if ($n instanceof IItem)
						return $n->$nm;
				}
				return $v;	
			}break;
		}
	}

	public function __toString(){
		$s = (string)$this->Nomenclature;
		if ($this->Article)
			return $s.' - '.$this->Article;
		$result = array();
		foreach ($this->Properties() as $p)
			if ($p->Value() &&
				$p->Name() != 'Price'
				&&
				$p->Name() != 'KitPrice'
				&&
				$p->Name() != 'Quantaty'
				&&
				$p->Name() != 'Reserved'
				&& (
				$p->Type() == TItemPropertyType::PT_STRING 
				|| 
				$p->Type() == TItemPropertyType::PT_DEC 
				|| 
				$p->Type() == TItemPropertyType::PT_INT
				)
			)
				$result[] = $p->Value();
		return $s.' '.join(',',$result);
	}
	
	public function OrderOptions(IItem $context){
		return $this->Model->ItemOrderOptions($this,$context) + $this->Model->ItemOrderOptions($this->Nomenclature,$context);
	}
	
	public function OrderPrice($kit = false, IItem $context = null){
		return $this->Model->OrderPrice($this->Nomenclature->Base,$this,$kit,$context);
	}		
}

class TCatalogProductModel extends TMSPublicableItemModel {
	
/**
 * @var TMSDiscountModel
 */
	protected $_ioc_discounts_model_;
	
/**
 * @var TMSArticleDiscountModel
 */
	protected $_ioc_article_discounts_model_;	
	
/**
 * @var bool
 */	
	protected $_bool_allow_reserved_order_ = true;
	
	protected function afterConstruct(){
		$this->_conf_classname_ = 'TMSArticle';
	}
	
	protected function postProcessProperties(){
		parent::postProcessProperties();
		$this->propertyMetas['Added']->ReadOnly = true;
	}
	
	protected function postProcessChildProperties($class){
		parent::postProcessChildProperties($class);		
		if (isset($this->childPropertyMetas[$class]['Reserved']))
			$this->childPropertyMetas[$class]['Reserved']->ReadOnly = true;
		
	}
	
	public function ProductUnavailableStates(){
		return $this->Adapter->Fetch(new TMSProductState());
	}
		
	protected function beforeSave(TNOrmObject $object){
		$object = parent::beforeSave($object);
			if (!$object->Added)
				$object->Added = new TDate();
		return $object;
	}

	public function ItemOrderOptions(IItem $item, IItem $context = null){
		if ($this->OrmSettings){
			if ($types = $this->OrmSettings->GetValue($item->ClassName().'.Order.Types')){
				$sels = $this->OrmSettings->GetValue($item->ClassName().'.Order.Selections');
				$capts = $this->OrmSettings->GetValue($item->ClassName().'.Order.Captions');
				$refclasses = $this->OrmSettings->GetValue($item->ClassName().'.Order.RefClasses');
				$result = array();
				foreach ($types as $nm => $t){
					if ($t == TItemPropertyType::PT_REFERENCE){
						$result[$nm] = new TOrmReferencePropertyMeta($nm, $capts[$nm], $refclasses[$nm],true);
						if (isset($sels[$nm])){
							if (is_array($sels[$nm]) || is_callable($sels[$nm]))
								$result[$nm]->SetSelection($sels[$nm]);
						}
					} else	
						$result[$nm] = new TPropertyMeta($nm, $capts[$nm], $t,isset($sels[$nm])?$sels[$nm]:array(),true);
				}
				return $result;
			}
		}
		return array();
	}
	
	public function  ItemDiscounts(IItem $article){
		$result = array();
		$explicit = $this->ArticleDiscountsModel->ArticleDiscounts($article);
		foreach ($explicit as $d)
			$result[$d->Discount->ItemId()] = $d;
		
		$implicit = $this->DiscountsModel->ImplicitProductDiscounts();
		foreach ($implicit as $d)
			if (!isset($result[$d->ItemId()]))
				if ($d->CanApplyToProduct($article)){
					$result[$d->ItemId()] = $this->ArticleDiscountsModel->GetDummyItem();
					$result[$d->ItemId()]->Article = $article;
					$result[$d->ItemId()]->Discount = $d;
				}	
		return $result;
	}	
	
	private function _calc_kit_options(array $kitprops, &$price, IItem $item){
		foreach ($kitprops as $pn){
			$p = $item->Property($pn);
			switch ($p->Type()){
				case TItemPropertyType::PT_REFERENCE:{
					if ($p->ReferedInstance() instanceof TMSProductItem || $p->ReferedInstance() instanceof TMSNomenclatureItem)
						$price += $p->ReferedInstance()->OrderPrice(true);
				}break;
				case TItemPropertyType::PT_COLLECTION:
				case TItemPropertyType::PT_SET:{
					foreach ($p->Value() as $prod)
						if ($prod instanceof TMSProductItem || $prod instanceof TMSNomenclatureItem)
						$price += $prod->OrderPrice(true);
				}break;
				default:$price += (float)$p->Value();break;
			}
		}		
	}
	
	public function OrderPrice(TMSNomenclature $n, IItem $item, $kit = false, IItem $context = null){
		switch ($n->PriceLogic){
			case TMSNomenclature::PRICE_ADD_KIT:
			case TMSNomenclature::PRICE_ONLY_KIT:{
				$price = 0;
				if ($n->PriceLogic == TMSNomenclature::PRICE_ADD_KIT)
					$price = $kit?($item->KitPrice?$item->KitPrice:$item->Price):$item->Price - $item->DiscountAmount;
				if ($this->OrmSettings){
					if ($kitprops = $this->OrmSettings->GetValue($item->ClassName().'.KitProperties'))
						$this->_calc_kit_options($kitprops, $price, $item);
					if ($kitprops = $this->OrmSettings->GetValue($item->Nomenclature->ClassName().'.KitProperties'))
						$this->_calc_kit_options($kitprops, $price, $item->Nomenclature);
					
					if ($kitprops = $this->OrmSettings->GetValue($this->Product->ClassName().'.Order.KitProperties')){
						$refclasses = $this->OrmSettings->GetValue($item->ClassName().'.Order.RefClasses') + $this->OrmSettings->GetValue(get_class($n).'.Order.RefClasses');
						foreach ($kitprops as $pn){
							$p = $context->Property($pn);
							switch ($p->Type()){
								case TItemPropertyType::PT_REFERENCE:{$price += $p->ReferedIntance()->OrderPrice(true);}break;
								case TItemPropertyType::PT_SET:
									if (isset($refclasses[$pn])){
										$dummy = new $refclasses[$pn]();	
										foreach ($p->Value() as $id){
											$dummy->Id = $id;
											$el = $this->WrapObject($this->Adapter->Load($dummy));
											if ($el instanceof TMSProductItem || $el instanceof TMSNomenclatureItem)
												$price += $el->OrderPrice(true);
										}
									}break;
								default:$price += (float)$p->Value();break;
							}
						}
					}					
				}
				return $price;
			}break;
		}
		return $kit?($item->KitPrice?$item->KitPrice:$item->Price):$item->Price - $item->DiscountAmount;
	}	
	
/**
 * @param object $object
 * @return TCandyItem
 */	
	public function WrapObject($object, array &$caller_stack = array()){
		if ($object instanceof $this->_conf_classname_)
			return new TMSProductItem($object,$this);
		if ($object instanceof TMSProductState) 
			return new TCandyItem($object,$this);
		return parent::WrapObject($object,$caller_stack);
	}	
}